package com.lhb.aicodemother.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.lhb.aicodemother.ai.core.AiCodeGeneratorFacade;
import com.lhb.aicodemother.ai.core.builder.VueProjectBuilder;
import com.lhb.aicodemother.ai.core.handler.StreamHandlerExecutor;
import com.lhb.aicodemother.ai.model.enums.CodeGenTypeEnum;
import com.lhb.aicodemother.constant.AppConstant;
import com.lhb.aicodemother.exception.BusinessException;
import com.lhb.aicodemother.exception.ErrorCode;
import com.lhb.aicodemother.exception.ThrowUtils;
import com.lhb.aicodemother.mapper.AppMapper;
import com.lhb.aicodemother.model.dto.app.AppQueryRequest;
import com.lhb.aicodemother.model.entity.App;
import com.lhb.aicodemother.model.entity.User;
import com.lhb.aicodemother.model.enums.ChatHistoryMessageTypeEnum;
import com.lhb.aicodemother.model.vo.AppVo;
import com.lhb.aicodemother.model.vo.UserVo;
import com.lhb.aicodemother.service.AppService;
import com.lhb.aicodemother.service.ChatHistoryService;
import com.lhb.aicodemother.service.ScreenshotService;
import com.lhb.aicodemother.service.UserService;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.util.SqlUtil;
import com.mybatisflex.spring.service.impl.ServiceImpl;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.io.File;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 应用 服务层实现。
 *
 * @author lihai
 */
@Service
@Slf4j
public class AppServiceImpl extends ServiceImpl<AppMapper, App>  implements AppService {

    @Resource
    private UserService userService;

    @Resource
    private AiCodeGeneratorFacade aiCodeGeneratorFacade;

    @Resource
    private ChatHistoryService chatHistoryService;

    @Resource
    private ScreenshotService screenshotService;

    @Resource
    private StreamHandlerExecutor streamHandlerExecutor;

    @Resource
    private VueProjectBuilder vueProjectBuilder;

    @Override
    public AppVo getAppVo(App app) {
        if(app == null){
            return null;
        }
        AppVo appVo = new AppVo();
        BeanUtils.copyProperties(app,appVo);
        User byId = userService.getById(app.getUserId());
        appVo.setUser(userService.getUserVo(byId));
        return appVo;

    }

    @Override
    public QueryWrapper getQueryWrapper(AppQueryRequest appQueryRequest) {
        if(appQueryRequest == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"请求参数为空");
        }
        Long id = appQueryRequest.getId();
        String appName = appQueryRequest.getAppName();
        Long userId = appQueryRequest.getUserId();
        String cover = appQueryRequest.getCover();
        Integer priority = appQueryRequest.getPriority();
        String codeGenType = appQueryRequest.getCodeGenType();
        String deployKey = appQueryRequest.getDeployKey();
        String sortField = appQueryRequest.getSortField();
        String sortOrder = appQueryRequest.getSortOrder();
        return QueryWrapper.create()
                .eq("id",id)
                .like("appName",appName)
                .like("cover",cover)
                .eq("codeGenType",codeGenType)
                .eq("deployKey",deployKey)
                .eq("userId",userId)
                .orderBy(sortField,"ascend".equals(sortOrder));
    }

    @Override
    public List<AppVo> getAppVoList(List<App> apps) {
        if(CollUtil.isEmpty(apps)){
            return new ArrayList<>();
        }
        // 获取到userId集合
        Set<Long> collect = apps.stream().map(App::getUserId).collect(Collectors.toSet());

        // 转化为Map
        Map<Long, UserVo> userVoMap = userService.listByIds(collect).stream().collect(Collectors.toMap(User::getId, userService::getUserVo));

        List<AppVo> list = apps.stream().map(app -> {
            AppVo appVo = this.getAppVo(app);
            UserVo userVo = userVoMap.get(app.getUserId());
            appVo.setUser(userVo);
            return appVo;
        }).toList();
        return list;
    }

    @Override
    public Flux<String> chatToGenCode(Long appId, String userMessage, User loginUser) {
        // 1.参数校验
        ThrowUtils.throwIf(appId == null || appId < 0,ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(StrUtil.isBlank(userMessage),ErrorCode.PARAMS_ERROR);
        // 2.判断该用户是否有权限
        App app = this.getById(appId);// 获取应用
        if(!app.getUserId().equals(loginUser.getId())){
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        // 获取应用类型
        CodeGenTypeEnum typeEnum = CodeGenTypeEnum.getValue(app.getCodeGenType());
        if(typeEnum == null){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"不支持的代码类型：" + app.getCodeGenType());
        }

        // 3.将用户消息添加到对话历史中
        chatHistoryService.addChatMessage(appId,userMessage, ChatHistoryMessageTypeEnum.USER.getValue(), loginUser.getId());

        // 4.调用ai生成代码
        Flux<String> stringFlux = aiCodeGeneratorFacade.generateAndSaveCodeStream(userMessage, typeEnum, appId);

        // 5.收集响应内容，并且在完成或者失败后保存到历史对话中
        return streamHandlerExecutor.doExecute(stringFlux,chatHistoryService,appId,loginUser,typeEnum);
//        StringBuilder aiResponse = new StringBuilder();
//        return stringFlux
//                .map(chunk -> {
//                    // 将响应内容拼接
//                    aiResponse.append(chunk);
//                    return chunk;
//                })
//                .doOnComplete(() -> {
//                    // 响应完成将ai返回内容保存到历史对话
//                    String aiResponseString = aiResponse.toString();
//                    if(StrUtil.isNotBlank(aiResponseString)){
//                        chatHistoryService.addChatMessage(appId,aiResponseString, ChatHistoryMessageTypeEnum.AI.getValue(), loginUser.getId());
//                    }
//                })
//                .doOnError(error -> {
//                    // 响应失败，将失败原因保存到历史对话
//                    String aiError = "Ai回复失败：" + error.getMessage();
//                    chatHistoryService.addChatMessage(appId,aiError, ChatHistoryMessageTypeEnum.AI.getValue(), loginUser.getId());
//                });
    }

    @Override
    public String deployApp(Long appId, User loginUser) {
        // 1.校验参数
        if(appId == null || appId < 0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        if(loginUser == null){
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }

        // 2.根据appId查询是否具备部署权限
        App app = this.getById(appId);
        if(!app.getUserId().equals(loginUser.getId())){
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }

        // 3.获取到部署应用的源目录
        String deployKey = app.getDeployKey();

        if(StrUtil.isBlank(deployKey)){
            // deployKey为空随机生成六位
            deployKey = RandomUtil.randomString(6);
        }
        String codeGenType = app.getCodeGenType();
        String sourceDirName = codeGenType + "_" + appId;
        String sourceDirPath = AppConstant.CODE_OUTPUT_ROOT_DIR + File.separator + sourceDirName;
        // 判断源目录是否存在
        File sourceFile = new File(sourceDirPath);
        if(!sourceFile.exists() || !sourceFile.isDirectory()){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"应用代码不存在，请先生成代码");
        }

        // Vue项目特殊处理：执行构建
        CodeGenTypeEnum typeEnum = CodeGenTypeEnum.getValue(codeGenType);
        if(typeEnum == CodeGenTypeEnum.VUE_PROJECT){
            boolean buildSuccess = vueProjectBuilder.buildProject(sourceDirPath);
            ThrowUtils.throwIf(!buildSuccess,ErrorCode.SYSTEM_ERROR,"Vue项目构建失败，请检查代码和依赖");
            // 检查dist目录是否存在
            File distDir = new File(sourceDirPath,"dist");
            ThrowUtils.throwIf(!distDir.exists(),ErrorCode.SYSTEM_ERROR,"Vue项目构建完成但未生成dist目录");
            // 将dist目录作为部署源
            sourceFile = distDir;
        }

        // 4.将源目录的数据复制一份到部署目录
        String deployDirPath = AppConstant.CODE_DEPLOY_ROOT_DIR + File.separator + deployKey;
        try{
            FileUtil.copyContent(sourceFile,new File(deployDirPath),true);
        }catch (Exception e){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"部署失败：" + e.getMessage());
        }

        // 5.更新应用的部署deployKey和部署时间
        App updateApp = new App();
        updateApp.setId(appId);
        updateApp.setDeployKey(deployKey);
        updateApp.setDeployedTime(LocalDateTime.now());
        boolean b = this.updateById(updateApp);
        if(!b){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }

        // 6。返回可访问的路径
        String appDeployUrl = String.format("%s/%s/",AppConstant.CODE_DEPLOY_HOST,deployKey);

        // 7.异步生成截图并更新应用封面
        generateAppScreenshotAsync(appId,appDeployUrl);
        return appDeployUrl;
    }

    @Override
    public void generateAppScreenshotAsync(Long appId, String appUrl) {
        // 使用虚拟线程异步执行
        Thread.startVirtualThread(() -> {
            // 调用截图服务生成截图并上传
            String screenshotUrl = screenshotService.generateAndUploadScreenshot(appUrl);
            // 更新应用封面字段
            App updateApp = new App();
            updateApp.setId(appId);
            updateApp.setCover(screenshotUrl);
            boolean update = this.updateById(updateApp);
            ThrowUtils.throwIf(!update,ErrorCode.OPERATION_ERROR,"更新应用封面字段失败");
        });
    }

    /*
    * 重写根据id删除应用 -- 关联删除对话历史
    * */
    @Override
    public boolean removeById(Serializable id) {
        if(id == null){
            return false;
        }
        Long appId = Long.valueOf(id.toString());
        try{
            chatHistoryService.deleteByAppId(appId);
        }catch (Exception e){
            log.error("删除应用关联对话历史失败：{}", e.getMessage());
        }
        return super.removeById(id);
    }
}
